package com.shenrongrong.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.*;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@Component
public class JwtUtil {
    // 1. 核心配置（建议后续迁移到配置文件，如 application.yml）
    private static final String SECRET = "your-storage-cabinet"; // 密钥：生产环境需使用复杂随机字符串，避免硬编码
    private static final String ISSUER = "SRR-app-storage";     // 发行人
    private static final long EXPIRATION_MS = 3600000;          // 过期时间：1小时（3600000 ms，修复原50分钟问题）

    /**
     * 生成带自定义声明的JWT
     * @param subject 用户唯一标识（如 username 或 userid，建议用字符串类型）
     * @param claims 自定义声明（如 userId、role 等）
     * @return JWT字符串
     */
    public static String generateToken(String subject, Map<String, String> claims) {
        // 校验必填参数
        if (subject == null || subject.trim().isEmpty()) {
            throw new IllegalArgumentException("subject（用户标识）不能为空");
        }

        JWTCreator.Builder builder = JWT.create()
                .withIssuer(ISSUER)                // 发行人（与验证时一致）
                .withSubject(subject)              // 用户标识（核心唯一字段）
                .withIssuedAt(new Date())          // 签发时间
                .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_MS)); // 过期时间

        // 批量添加自定义声明（若 claims 为 null 则跳过，避免 NPE）
        if (Objects.nonNull(claims) && !claims.isEmpty()) {
            claims.forEach(builder::withClaim);
        }

        // 用 HMAC256 算法签名（密钥必须与验证时一致）
        return builder.sign(Algorithm.HMAC256(SECRET));
    }

    /**
     * 验证Token并返回解析后的完整对象（包含所有声明和元数据）
     * @param token JWT字符串
     * @return DecodedJWT 解析后的Token对象
     * @throws TokenExpiredException Token已过期
     * @throws SignatureVerificationException 签名错误（Token被篡改或密钥不匹配）
     * @throws InvalidClaimException 声明无效（如发行人不匹配）
     * @throws JWTDecodeException Token格式错误
     * @throws IllegalArgumentException Token为空
     */
    public static DecodedJWT verifyAndDecode(String token) {
        // 1. 非空校验
        if (token == null || token.trim().isEmpty()) {
            throw new IllegalArgumentException("Token不能为空,请先登录");
        }

        // 2. 构建验证器（指定算法和发行人）
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer(ISSUER) // 校验发行人是否一致
                .build();

        // 3. 验证并返回解析结果（会自动抛出对应异常）
        return verifier.verify(token);
    }

    /**
     * 快速验证Token有效性（仅返回布尔值，不抛出异常）
     * @param token JWT字符串
     * @return true：有效；false：无效（空、过期、签名错误等）
     */
    public static boolean validateToken(String token) {
        try {
            verifyAndDecode(token);
            return true;
        } catch (Exception e) {
            // 所有验证失败的情况均返回false
            return false;
        }
    }

    /**
     * 从有效Token中提取自定义声明（如userId、role）
     * @param token JWT字符串
     * @return 自定义声明Map（无声明则返回空Map）
     * @throws Exception 若Token无效，抛出对应异常（可通过validateToken先校验）
     */
    public static Map<String, String> getClaims(String token) throws Exception {
        DecodedJWT decodedJWT = verifyAndDecode(token);
        Map<String, String> claims = new HashMap<>();
        // 排除JWT默认声明（iss、sub、iat、exp），只保留自定义声明
        decodedJWT.getClaims().forEach((key, claim) -> {
            if (!"iss".equals(key) && !"sub".equals(key) && !"iat".equals(key) && !"exp".equals(key)) {
                claims.put(key, claim.asString()); // 假设自定义声明均为字符串类型
            }
        });
        return claims;
    }

    /**
     * 从有效Token中提取subject（用户标识）
     * @param token JWT字符串
     * @return 用户标识（如username或userid）
     * @throws Exception 若Token无效，抛出对应异常
     */
    public static String getSubject(String token) throws Exception {
        return verifyAndDecode(token).getSubject();
    }

    /**
     * 从有效Token中提取指定的自定义声明（如"userId"）
     * @param token JWT字符串
     * @param claimKey 声明键名（如"userId"）
     * @return 声明值（若不存在则返回null）
     * @throws Exception 若Token无效，抛出对应异常
     */
    public static String getClaimByKey(String token, String claimKey) throws Exception {
        if (claimKey == null || claimKey.trim().isEmpty()) {
            throw new IllegalArgumentException("声明键名不能为空");
        }
        DecodedJWT decodedJWT = verifyAndDecode(token);
        return decodedJWT.getClaim(claimKey).asString();
    }


    // 测试方法
    public static void main(String[] args) {
        // 1. 生成Token
        Map<String, String> userClaims = new HashMap<>();
        userClaims.put("userId", "1001");
        userClaims.put("role", "user");
        String token = JwtUtil.generateToken("zhangsan", userClaims);
        System.out.println("生成的Token: " + token + "\n");

        // 2. 验证并解析Token
        try {
            // 快速验证有效性
            System.out.println("Token是否有效: " + JwtUtil.validateToken(token));

            // 提取用户标识
            String subject = JwtUtil.getSubject(token);
            System.out.println("用户标识(subject): " + subject);

            // 提取指定声明
            String userId = JwtUtil.getClaimByKey(token, "userId");
            String role = JwtUtil.getClaimByKey(token, "role");
            System.out.println("用户ID: " + userId);
            System.out.println("用户角色: " + role);

            // 提取所有自定义声明
            Map<String, String> claims = JwtUtil.getClaims(token);
            System.out.println("所有自定义声明: " + claims);

        } catch (TokenExpiredException e) {
            System.out.println("Token已过期: " + e.getMessage());
        } catch (SignatureVerificationException e) {
            System.out.println("签名错误（Token可能被篡改）: " + e.getMessage());
        } catch (InvalidClaimException e) {
            System.out.println("声明无效（如发行人不匹配）: " + e.getMessage());
        } catch (JWTDecodeException e) {
            System.out.println("Token格式错误: " + e.getMessage());
        } catch (Exception e) {
            System.out.println("Token验证失败: " + e.getMessage());
        }
    }
}